using System;
using System.IO;
using Velocity4Net.Errors;
using Velocity4Net.Runtime.Logging;
using Velocity4Net.Cfg;

namespace Velocity4Net.Runtime.ResourceManage.Loader
{
    /**
     * This is abstract class the all text resource loaders should
     * extend.
     *
     * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
     * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
     * @version $Id: ResourceLoader.java 832280 2009-11-03 02:47:55Z wglass $
     */
    public abstract class ResourceLoader
    {
        /**
         * Does this loader want templates produced with it
         * cached in the Runtime.
         */
        protected bool isCachingOn = false;

        /**
         * This property will be passed on to the templates
         * that are created with this loader.
         */
        protected long modificationCheckInterval = 2;

        /**
         * Class name for this loader, for logging/debuggin
         * purposes.
         */
        protected String className = null;

        protected RuntimeServices rsvc = null;
        protected Log log = null;

        /**
         * This initialization is used by all resource
         * loaders and must be called to set up common
         * properties shared by all resource loaders
         * @param rs
         * @param configuration
         */
        public void commonInit(RuntimeServices rs, VelocitySettings configuration)
        {
            this.rsvc = rs;
            this.log = rsvc.getLog();

            /*
             *  these two properties are not required for all loaders.
             *  For example, for ClasspathLoader, what would cache mean?
             *  so adding default values which I think are the safest
             *
             *  don't cache, and modCheckInterval irrelevant...
             */

            try
            {
                isCachingOn = configuration.GetBoolean("cache", false);
            }
            catch (System.Exception e)
            {
                isCachingOn = false;
                String msg = "Exception parsing cache setting: " + configuration.GetString("cache");
                log.error(msg, e);
                throw new VelocityException(msg, e);
            }
            try
            {
                modificationCheckInterval = configuration.GetLong("modificationCheckInterval", 0);
            }
            catch (System.Exception e)
            {
                modificationCheckInterval = 0;
                String msg = "Exception parsing modificationCheckInterval setting: " + configuration.GetString("modificationCheckInterval");
                log.error(msg, e);
                throw new VelocityException(msg, e);
            }

            /*
             * this is a must!
             */
            className = typeof(ResourceCacheImpl).Name;
            try
            {
                className = configuration.GetString("class", className);
            }
            catch (System.Exception e)
            {
                String msg = "Exception retrieving resource cache class name";
                log.error(msg, e);
                throw new VelocityException(msg, e);
            }
        }

        /**
         * Initialize the template loader with a
         * a resources class.
         * @param configuration
         */
        public abstract void Init(VelocitySettings configuration);

        /**
         * Get the InputStream that the Runtime will parse
         * to create a template.
         * @param source
         * @return The input stream for the requested resource.
         * @throws ResourceNotFoundException
         */
        public abstract Stream GetResourceStream(String source);

        /**
         * Given a template, check to see if the source of InputStream
         * has been modified.
         * @param resource
         * @return True if the resource has been modified.
         */
        public abstract bool isSourceModified(Resource resource);

        /**
         * Get the last modified time of the InputStream source
         * that was used to create the template. We need the template
         * here because we have to extract the name of the template
         * in order to locate the InputStream source.
         * @param resource
         * @return Time in millis when the resource has been modified.
         */
        public abstract long getLastModified(Resource resource);

        /**
         * Return the class name of this resource Loader
         * @return Class name of the resource loader.
         */
        public String ClassName
        {
            get
            {
                return className;
            }
        }


        /**
         * The Runtime uses this to find out whether this
         * template loader wants the Runtime to cache
         * templates created with InputStreams provided
         * by this loader.
         * @return True if this resource loader caches.
         */
        public bool CachingOn
        {
            get
            {
                return isCachingOn;
            }
            set
            {
                isCachingOn = value;
            }
        }

        /**
         * Get the interval at which the InputStream source
         * should be checked for modifications.
         * @return The modification check interval.
         */
        public long ModificationCheckInterval
        {
            get
            {
                return modificationCheckInterval;
            }
            set
            {
                modificationCheckInterval = value;
            }
        }

        /**
         * Check whether any given resource exists. This is not really
         * a very efficient test and it can and should be overridden in the
         * subclasses extending ResourceLoader. 
         *
         * @param resourceName The name of a resource.
         * @return true if a resource exists and can be accessed.
         * @since 1.6
         */
        public bool resourceExists(String resourceName)
        {
            Stream _is = null;
            try
            {
                _is = GetResourceStream(resourceName);
            }
            catch (ResourceNotFoundException e)
            {
                if (log.isDebugEnabled())
                {
                    log.debug("Could not load resource '" + resourceName
                            + "' from ResourceLoader " + this.GetType().Name
                            + ": " + e.Message);
                }
            }
            finally
            {
                try
                {
                    if (_is != null)
                    {
                        _is.Close();
                    }
                }
                catch (System.Exception e)
                {
                    if (log.isErrorEnabled())
                    {
                        String msg = "While closing InputStream for resource '" + resourceName
                            + "' from ResourceLoader " + this.GetType().Name;
                        log.error(msg, e);
                        throw new VelocityException(msg, e);
                    }
                }
            }
            return (_is != null);
        }
    }
}
